package com.utopia.keyvalue.store.impl.disk;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import com.utopia.keyvalue.store.Configure;
import com.utopia.keyvalue.utils.RuntimeExceptionWraper;

/**
 * @author yanjiaqiang
 * @date 2011-3-16
 * @version $Id: DataStore.java 5 2011-03-22 09:23:42Z JiaqiangYan@gmail.com $
 */
public class DataStore {
	private ArrayList<RandomAccessFile> rafList = new ArrayList<RandomAccessFile>();
	private AtomicInteger currentFileIndex = new AtomicInteger();
	private File dir;
	// 1GB
	long MAX_SIZE_PER_FILE = Configure.getDataMaxSizePerFile();

	public DataStore() {
		super();
		this.dir = new File(Configure.getDataBaseDir());
		try {
			if (!dir.exists()) {
				dir.mkdirs();
				createNewDataFile();
			} else {
				FilenameFilter filter = new FilenameFilter() {
					@Override
					public boolean accept(File dir, String name) {
						return name.endsWith(Configure.DATA_FILE_SUFFIX);
					}
				};

				File[] files = dir.listFiles(filter);
				if (files == null || files.length == 0) {
					createNewDataFile();
				} else {
					for (int i = 0; i < files.length; i++) {
						RandomAccessFile raf = new RandomAccessFile(files[i], "rw");
						raf.seek(files[i].length());
						rafList.add(raf);
					}
				}
			}
			currentFileIndex.addAndGet(rafList.size());
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void put(DataEntry data) {
		byte[] bs = data.toBytes();
		put(bs);
	}

	private void put(byte[] data) {
		try {
			getCurrentRandomAccessFile().write(data);
			if (getCurrentRandomAccessFile().length() >= MAX_SIZE_PER_FILE) {
				createNewDataFile();
				currentFileIndex.incrementAndGet();
				
			}
		} catch (IOException e) {
			RuntimeExceptionWraper.wrap(e);
		}

	}

	public byte[] remove(int fileIndex, int offset) {
		try {
			if (fileIndex > currentFileIndex.intValue() || offset < 0) {
				throw new IllegalArgumentException();
			}
			RandomAccessFile raf = getRandomAccessFile(fileIndex);
			raf.seek(offset);
			raf.write(DataEntry.DELETED);
			int dataLen = raf.readInt();
			byte[] data = new byte[dataLen];
			raf.read(data);
			return data;

		} catch (IOException e) {
			RuntimeExceptionWraper.wrap(e);
		}
		return null;
	}

	public DataEntry get(int fileIndex, int offset) {
		try {
			if (fileIndex > currentFileIndex.intValue() || offset < 0) {
				throw new IllegalArgumentException();
			}
			RandomAccessFile raf = getRandomAccessFile(fileIndex);
			raf.seek(offset);

			int delteFlag = raf.read();
			int dataLen = raf.readInt();
			byte[] data = new byte[dataLen];
			raf.read(data);

			ByteBuffer buf = ByteBuffer.allocate(1 + 4 + data.length);
			buf.put((byte) delteFlag);
			buf.putInt(dataLen);
			buf.put(data);

			DataEntry entity = new DataEntry();
			entity.fromBytes(buf.array());
			return entity;

		} catch (IOException e) {
			RuntimeExceptionWraper.wrap(e);
		}
		return null;
	}

	private void createNewDataFile() {
		try {
			RandomAccessFile raf = new RandomAccessFile(getFilepath(), "rw");
			rafList.add(raf);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

	}

	private String getFilepath() {
		return dir.getAbsoluteFile() + File.separator + "data_" + currentFileIndex.intValue()
				+ Configure.DATA_FILE_SUFFIX;
	}

	private RandomAccessFile getRandomAccessFile(int index) {
		return rafList.get(index - 1);
	}

	private RandomAccessFile getCurrentRandomAccessFile() {
		return getRandomAccessFile(currentFileIndex.intValue());
	}

	public int getCurrentFileIndex() {
		return currentFileIndex.intValue();
	}

	public int getCurrentFilePosOffset() {
		try {
			return (int) getCurrentRandomAccessFile().getFilePointer();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return -1;
	}

	public void close() {
		for (RandomAccessFile raf : rafList) {
			try {
				raf.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}
